import { OnInit, Input, Injector, ChangeDetectorRef, Type } from '@angular/core'
import { ControlValueAccessor } from '@angular/forms'
import { Observable, BehaviorSubject, merge } from 'rxjs'
import { debounceTime, switchMap, map, tap } from 'rxjs/operators'
import { toBoolean } from 'ng-zorro-antd'

import { IGenericPagination } from '@shared/interfaces/pagination'

export abstract class AbstractCustomSelectComponent<T = any, P = {}> implements OnInit, ControlValueAccessor {
  constructor(
    public injector: Injector,
  ) {
    this.cdRef = injector.get<ChangeDetectorRef>(ChangeDetectorRef as Type<ChangeDetectorRef>)
  }

  @Input()
  set value(value: any) {
    this._value = value
    this.notifyValueChange()
  }

  get value(): any {
    return this._value as T
  }

  @Input()
  set required(value: boolean) {
    this._required = toBoolean(value)
  }

  get required(): boolean {
    return this._required
  }

  @Input() disabled: boolean
  @Input() placeholder: string
  @Input() allowClear = true

  protected _value: any
  protected _required: boolean
  protected cdRef: ChangeDetectorRef
  protected _options: T[] = []

  public sortFn = null
  public isLoading = false

  searchChange$ = new BehaviorSubject(this.getParams())
  refreshRequest$ = new BehaviorSubject('')
  options$: Observable<T[]>

  onChange: (value: T) => {}
  onTouched: () => {}

  ngOnInit(): void {
    this.options$ = merge(
      // 搜索变化，有节流功能
      this.searchChange$.asObservable().pipe(
        debounceTime(500),
        switchMap(params => this.query(params)),
        map(response => {
          this._options = [...this._options, ...response.data]
          this.isLoading = false
          return this._options
        }),
      ),
      this.refreshRequest$.asObservable().pipe(
        map(() => {
          return this._options
        }),
      ),
    ).pipe(
      map(options => this.deduplicate(options)),
      map(options => {
        if (this.sortFn) {
          options.sort(this.sortFn())
        }
        return options
      }),
    )

    this.searchChange$.next(this.getParams())
  }

  // 选项去重
  abstract deduplicate(list: T[]): T[]

  // 查询参数
  abstract query(params: P): Observable<IGenericPagination<T>>

  // TODO: 应该可以是可选的
  abstract getParams(): P

  notifyValueChange(): void {
    if (this.onChange) {
      this.onChange(this.value)
    }
  }

  writeValue(value: T): void {
    this._value = value
    if (value) {
      this._options = [value, ...this._options]
      this.refreshRequest$.next('refresh')
    }
    setTimeout(() => this.cdRef.detectChanges(), 0)
  }

  registerOnChange(fn: any): void {
    this.onChange = fn
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn
  }

  setDisabledState(isDisabled: boolean): void {
    this.disabled = isDisabled
  }
}
